﻿/*  Copyright 2012 PerceiveIT Limited
 *  This file is part of the Scryber library.
 *
 *  You can redistribute Scryber and/or modify 
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  Scryber is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Scryber source code in the COPYING.txt file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using Scryber.Text;
using Scryber.Drawing;
using System.ComponentModel;

namespace Scryber.Styles.Old
{
    /// <summary>
    /// Defines a single style with inner StyleItems that can be applied to an Component
    /// </summary>
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class PDFStyle : PDFStyleBase
    {
        //
        // const
        //

        #region public const string PDFStylesNamespace

        /// <summary>
        /// The fully qualified assemblynamespace
        /// </summary>
        public const string PDFStylesNamespace = "Scryber.Styles, Scryber.Styles, Version=0.9.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe";

        #endregion

        //
        // inner classes
        //

        #region private class ImmutableStyleData

        /// <summary>
        /// Holds locally cached copies of the immutable style data
        /// </summary>
        private class ImmutableStyleData
        {
            public PDFTextRenderOptions Text;
            public PDFPositionOptions Position;
            public PDFStyle Flattened;
        }

        #endregion

        #region private class PDFUniqueStyle

        /// <summary>
        /// Inner private class to retain the ordering of Items in the style whilst flattening.
        /// </summary>
        private class PDFUniqueStyle : System.Collections.ObjectModel.KeyedCollection<PDFObjectType, PDFStyleItem>
        {
            protected override PDFObjectType GetKeyForItem(PDFStyleItem item)
            {
                return item.Type;
            }

            public IEnumerable<PDFStyleItem> Values
            {
                get { return this.Items; }
            }
        }

        #endregion

        //
        // properties
        //

        #region public string ID {get;set;}

        private string _id;

        /// <summary>
        /// Gets or sets the ID for this instance
        /// </summary>
        [PDFAttribute("id")]
        public string ID
        {
            get
            {
                if (String.IsNullOrEmpty(_id))
                {
                    _id = string.Empty;
                }
                return this._id;
            }
            set
            {
                _id = value;
            }
        }

        #endregion

        #region public bool Immutable {get;set;}

        private bool _isimmutable = false;

        /// <summary>
        /// local reference for the immutable data in this style
        /// </summary>
        private ImmutableStyleData _immutableData;

        /// <summary>
        /// Gets or sets the flag that marks this style as immutable
        /// </summary>
        public bool Immutable
        {
            get { return this._isimmutable; }
            set { this._isimmutable = value; _immutableData = null; }
        }

        #endregion


        //
        // style item accessors
        //

        #region public PDFMarginsStyle Margins {get;}

        /// <summary>
        /// Gets the margins associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFMarginsStyle Margins
        {
            get
            {
                PDFMarginsStyle margin = this.GetStyleItem(PDFObjectTypes.StyleMargins,false) as PDFMarginsStyle;
                if(null == margin)
                {
                    margin = new PDFMarginsStyle();
                    this.Add(margin);
                }
                return margin;
            }
        }

        #endregion

        #region public PDFPaddingStyle Padding {get;}
        
        /// <summary>
        /// Gets the padding associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFPaddingStyle Padding
        {
            get
            {
                PDFPaddingStyle pad = this.GetStyleItem(PDFObjectTypes.StylePadding,false) as PDFPaddingStyle;
                if (null == pad)
                {
                    pad = new PDFPaddingStyle();
                    this.Add(pad);
                }
                return pad;
            }
        }

        #endregion

        #region public PDFBackgroundStyle Background {get;}

        /// <summary>
        /// Gets the background associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFBackgroundStyle Background
        {
            get
            {
                PDFBackgroundStyle style = this.GetStyleItem(PDFObjectTypes.StyleBackground, false) as PDFBackgroundStyle;
                if (null == style)
                {
                    style = new PDFBackgroundStyle();
                    this.Add(style);
                }
                return style;
            }
        }

        #endregion

        #region public PDFBorderStyle Border {get;}

        /// <summary>
        /// Gets the padding associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFBorderStyle Border
        {
            get
            {
                PDFBorderStyle bord = this.GetStyleItem(PDFObjectTypes.StyleBorder, false) as PDFBorderStyle;
                if (null == bord)
                {
                    bord = new PDFBorderStyle();
                    this.Add(bord);
                }
                return bord;
            }
        }

        #endregion

        #region public PDFFillStyle Fill {get;}

        /// <summary>
        /// Gets the fill associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFFillStyle Fill
        {
            get
            {
                PDFFillStyle f = this.GetStyleItem(PDFObjectTypes.StyleFill, false) as PDFFillStyle;
                if (null == f)
                {
                    f = new PDFFillStyle();
                    this.Add(f);
                }
                return f;
            }
        }

        #endregion

        #region public PDFFontStyle Font {get;}

        /// <summary>
        /// Gets the Font associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFFontStyle Font
        {
            get
            {
                PDFFontStyle f = this.GetStyleItem(PDFObjectTypes.StyleFont, false) as PDFFontStyle;
                if (null == f)
                {
                    f = new PDFFontStyle();
                    this.Add(f);
                }
                return f;
            }
        }

        #endregion

        #region public PDFPageStyle PageStyle {get;}

        /// <summary>
        /// Gets the page options associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFPageStyle PageStyle
        {
            get
            {
                PDFPageStyle pap = this.GetStyleItem(PDFObjectTypes.StylePage, false) as PDFPageStyle;
                if (null == pap)
                {
                    pap = new PDFPageStyle();
                    this.Add(pap);
                }
                return pap;
            }
        }

        #endregion

        #region public PDFPositionStyle Position {get;}

        /// <summary>
        /// Gets the position associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFPositionStyle Position
        {
            get
            {
                PDFPositionStyle pos = this.GetStyleItem(PDFObjectTypes.StylePosition, false) as PDFPositionStyle;
                if (null == pos)
                {
                    pos = new PDFPositionStyle();
                    this.Add(pos);
                }
                return pos;
            }
        }

        #endregion

        #region public PDFStrokeStyle Stroke {get;}

        /// <summary>
        /// Gets the stroke associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFStrokeStyle Stroke
        {
            get
            {

                PDFStrokeStyle str = this.GetStyleItem(PDFObjectTypes.StyleStroke, false) as PDFStrokeStyle;
                if (null == str)
                {
                    str = new PDFStrokeStyle();
                    this.Add(str);
                }
                return str;
            }
        }

        #endregion

        #region public PDFTextStyle Text {get;}

        /// <summary>
        /// Gets the text style associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFTextStyle Text
        {
            get
            {
                PDFTextStyle txt = this.GetStyleItem(PDFObjectTypes.StyleText, false) as PDFTextStyle;
                if (null == txt)
                {
                    txt = new PDFTextStyle();
                    this.Add(txt);
                }
                return txt;
            }
        }

        #endregion

        #region public PDFShapeStyle {get;}

        /// <summary>
        /// Gets the shape style options applied in this style.
        /// </summary>
        public PDFShapeStyle Shape
        {
            get
            {
                PDFShapeStyle shape = this.GetStyleItem(PDFObjectTypes.ShapeStyle, false) as PDFShapeStyle;
                if (null == shape)
                {
                    shape = new PDFShapeStyle();
                    this.Add(shape);
                }
                return shape;
            }
            
        }

        #endregion

        #region public PDFTableStyle Table {get;}

        /// <summary>
        /// Gets the text style associated with this style - if it is not currently defined a new item is created, added to this style then returned
        /// </summary>
        public PDFTableStyle Table
        {
            get
            {
                PDFTableStyle tbl = this.GetStyleItem(PDFObjectTypes.StyleTable, false) as PDFTableStyle;
                if (null == tbl)
                {
                    tbl = new PDFTableStyle();
                    this.Add(tbl);
                }
                return tbl;
            }
        }

        #endregion

        #region public PDFOverflowStyle Overflow {get;}

        public PDFOverflowStyle Overflow
        {
            get
            {
                PDFOverflowStyle over = this.GetStyleItem(PDFObjectTypes.StyleOverflow, false) as PDFOverflowStyle;
                if (null == over)
                {
                    over = new PDFOverflowStyle();
                    this.Add(over);
                }
                return over;
            }
        }

        #endregion

        #region public PDFOutlineStyle Outline {get;}
        /// <summary>
        /// Gets the outline style options for this Style
        /// </summary>
        public PDFOutlineStyle Outline
        {
            get
            {
                PDFOutlineStyle outline = this.GetStyleItem(PDFObjectTypes.StyleOutline, false) as PDFOutlineStyle;
                if (null == outline)
                {
                    outline = new PDFOutlineStyle();
                    this.Add(outline);
                }
                return outline;
            }
        }

        #endregion

        #region public PDFColumnsStyle Columns

        /// <summary>
        /// Gets the Columns associated with this style
        /// </summary>
        public PDFColumnsStyle Columns
        {
            get
            {
                PDFColumnsStyle cols = this.GetStyleItem(PDFObjectTypes.StyleColumns, false) as PDFColumnsStyle;
                if (null == cols)
                {
                    cols = new PDFColumnsStyle();
                    this.Add(cols);
                }
                return cols;
            }
        }

        #endregion

        #region public PDFListStyle List {get;}

        /// <summary>
        /// Gets the list options associated with this style
        /// </summary>
        public PDFListStyle List
        {
            get
            {
                PDFListStyle style = this.GetStyleItem(PDFObjectTypes.StyleList, false) as PDFListStyle;
                if (null == style)
                {
                    style = new PDFListStyle();
                    this.Add(style);
                }
                return style;
            }
        }

        #endregion

        #region public PDFClipStyle Clipping {get;}

        /// <summary>
        /// Gets the clipping options associated with this style
        /// </summary>
        public PDFClipStyle Clipping
        {
            get
            {
                PDFClipStyle style = this.GetStyleItem(PDFObjectTypes.StyleClip, false) as PDFClipStyle;
                if (null == style)
                {
                    style = new PDFClipStyle();
                    this.Add(style);
                }
                return style;
            }
        }

        #endregion

        #region public PDFModifyPageStyle PageModifications {get;}

        /// <summary>
        /// Gets the Page Update options in this style
        /// </summary>
        public PDFModifyPageStyle PageModifications
        {
            get
            {
                PDFModifyPageStyle style = this.GetStyleItem(PDFObjectTypes.StyleModify, false) as PDFModifyPageStyle;
                if (null == style)
                {
                    style = new PDFModifyPageStyle();
                    this.Add(style);
                }
                return style;
            }
  
        }

        #endregion


        #region protected PDFStyleItemCollection Items {get} + HasItems {get;}

        private PDFStyleItemCollection _items = null;

        /// <summary>
        /// Protected accessor to the style items defined in this style
        /// </summary>
        [PDFArray(typeof(PDFStyleItem))]
        [PDFElement("")]
        public PDFStyleItemCollection Items
        {
            get
            {
                if (this._items == null)
                    this._items = new PDFStyleItemCollection();
                return this._items;
            }
        }

        /// <summary>
        /// Returns true if this style has any defined style items
        /// </summary>
        public bool HasItems
        {
            get { return this._items != null && this._items.Count > 0; }
        }

        #endregion


        //
        // .ctor(s)
        //

        #region public PDFStyle()

        /// <summary>
        /// Creates a new empty instance of a PDFStyle
        /// </summary>
        public PDFStyle()
            : this(PDFObjectTypes.Style)
        {
        }

        #endregion

        #region protected PDFStyle(PDFObjectType type)

        /// <summary>
        /// Creates a new instance of a PDFStyle with the specified type. Only Inheritors can call this method
        /// </summary>
        /// <param name="type"></param>
        protected PDFStyle(PDFObjectType type)
            : base(type)
        {
        }

        #endregion

        //
        // methods
        //

        #region public void Add(PDFStyleItem item)

        /// <summary>
        /// Adds the specified item to this style
        /// </summary>
        /// <param name="item"></param>
        public void Add(PDFStyleItem item)
        {
            if (null == item)
                throw new ArgumentNullException("item");

            if (this.Immutable)
                throw new InvalidOperationException("This style is immutable and cannot be modified");

            this.Items.Add(item);
        }

        #endregion

        #region  public bool Remove(PDFStyleItem item) + 1 overload

        /// <summary>
        /// removes the specified item from this style
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Remove(PDFStyleItem item)
        {
            if (null == item)
                return false;
            else if(this.Immutable)
                throw new InvalidOperationException("This style is immutable and cannot be modified");
            else
                return this.Items.Remove(item);
        }

        /// <summary>
        /// Removes all items of the specified type in this style
        /// </summary>
        /// <param name="itemtype"></param>
        /// <returns></returns>
        public bool Remove(PDFObjectType itemtype)
        {
            if (this.Immutable)
                throw new InvalidOperationException("This style is immutable and cannot be modified");

            bool removed = false;
            for (int i = this.Items.Count - 1; i >= 0; i--)
            {
                PDFStyleItem item = this.Items[i];
                if(item.Type == itemtype)
                {
                    this.Items.RemoveAt(i);
                    removed = true;
                }
            }
            return removed;
        }

        #endregion

        #region public void MergeInto(PDFStyle style)

        /// <summary>
        /// Merges all style items in this instance into the specified style. 
        /// NOTE: does not flatten the items so there may be duplicates.
        /// Once all the required merging has been done, then it should be 
        /// flattened to give a clean single set of items.
        /// </summary>
        /// <param name="style"></param>
        public void MergeInto(PDFStyle style)
        {
            if (this.HasItems)
            {
                foreach (PDFStyleItem item in this.Items)
                {
                    style.MergeItem(item);
                }
            }
        }

        #endregion

        #region public override void MergeInto(PDFStyle style, IPDFComponent Component, ComponentState state)

        /// <summary>
        /// Merges any appropriate style items into the provided style based on the specified component and state)
        /// </summary>
        /// <param name="style">The style to merge into.</param>
        /// <param name="Component">The component whose style </param>
        /// <param name="state">The state of the component</param>
        public override void MergeInto(PDFStyle style, IPDFComponent Component, ComponentState state)
        {
            if (this.HasItems)
            {
                foreach (PDFStyleItem item in this.Items)
                {
                    style.MergeItem(item);
                }
            }
        }

        #endregion

        #region public override PDFStyle MatchClass(string classname)

        /// <summary>
        /// Matches this style against the provided name.
        /// </summary>
        /// <param name="classname"></param>
        /// <returns></returns>
        /// <remarks>Overrides the base implementation to always retrun null (no class names on the PDFStyle class)</remarks>
        public override PDFStyle MatchClass(string classname)
        {
            return null;
        }

        #endregion


        #region public virtual PDFStyle Flatten()

        /// <summary>
        /// Flattens the style to single definitions 
        /// </summary>
        /// <returns></returns>
        public virtual PDFStyle Flatten()
        {
            if (this.Immutable)
            {
                if (null != this._immutableData && null != this._immutableData.Flattened)
                    return _immutableData.Flattened;
            }

            PDFStyle style = new PDFStyle();
            if (this.HasItems)
            {
                PDFUniqueStyle dict = new PDFUniqueStyle();
                for (int i = 0; i < this.Items.Count; i++)
                {
                    PDFStyleItem item = this.Items[i];
                    if (dict.Contains(item.Type) == false)
                        dict.Add(item);
                    else
                    {
                        PDFStyleItem exist = dict[item.Type];
                        item.MergeInto(exist);
                    }
                }

                PDFStyleItemCollection col = new PDFStyleItemCollection();
                col.AddRange(dict.Values);
                style._items = col;
            }

            if (this.Immutable)
            {
                if (null == this._immutableData)
                    this._immutableData = new ImmutableStyleData();
                this._immutableData.Flattened = style;
            }

            return style;
        }

        #endregion

        #region protected virtual void MergeItem(PDFStyleItem item)

        /// <summary>
        /// Merges the item specified into this instance
        /// </summary>
        /// <param name="item"></param>
        protected virtual void MergeItem(PDFStyleItem item)
        {
            this.Items.Add(item.Clone());
        }

        #endregion

        #region public void MergeInherited(PDFStyle style, IPDFComponent Component, bool replace)

        /// <summary>
        /// Merges all the items that are inherited in this instance into the specified style.
        /// Base implementation ignores any component or the replace flag.
        /// </summary>
        /// <param name="style"></param>
        /// <param name="Component"></param>
        /// <param name="replace"></param>
        public void MergeInherited(PDFStyle style, IPDFComponent Component, bool replace)
        {
            if (this.HasItems)
            {
                foreach (PDFStyleItem item in this.Items)
                {
                    if (item.IsInherited)
                        style.MergeItem(item);
                }
            }
        }

        #endregion

        #region public PDFStyleItem GetStyleItem(PDFObjectType type, bool flatten)

        /// <summary>
        /// Returns any style item defined in this instance with the associated type. 
        /// If flatten then a merged clone of all the items in this instance will be returned.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="flatten"></param>
        /// <returns></returns>
        public PDFStyleItem GetStyleItem(PDFObjectType type, bool flatten)
        {
            PDFStyleItem psi = null;
            if (this.Immutable)
            {
                foreach (PDFStyleItem item in this.Flatten().Items)
                {
                    if (item.Type == type)
                        return item;
                }
                return null;
            }
            else
            {
                foreach (PDFStyleItem item in this.Items)
                {
                    if (item.Type == type)
                    {
                        if (psi == null)
                        {
                            if (flatten)
                                psi = item.Clone();
                            else
                            {
                                psi = item;
                                break;
                            }
                        }
                        else if (flatten)
                            item.MergeInto(psi);
                    }
                }
            }
            return psi;
        }

        #endregion

        #region public bool IsDefined(PDFObjectType type)

        /// <summary>
        /// Returns true if this style has (contains) the style item with the specified type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public bool IsDefined(PDFObjectType type)
        {
            foreach (PDFStyleItem si in this.Items)
            {
                if (si.Type == type)
                    return true;
            }
            return false;
        }

        #endregion

        #region public PDFStyle Clone()

        /// <summary>
        /// Returns a clone of this style and clones of all the items in this style
        /// </summary>
        /// <returns></returns>
        public PDFStyle Clone()
        {
            PDFStyle style = base.MemberwiseClone() as PDFStyle;
            style._items = new PDFStyleItemCollection();

            foreach (PDFStyleItem si in this.Items)
            {
                style.Add(si.Clone());
            }
            return style;
        }

        #endregion

        #region public override string ToString()

        /// <summary>
        /// Overrides the default ToString metho to return a concatenation of the type and ID of this style.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Type.ToString() + ":" + this.ID;
        }

        #endregion

        //
        // TryGetXXX
        //


        public bool TryGetMargins(out PDFMarginsStyle margins)
        {
            margins = this.GetStyleItem(PDFObjectTypes.StyleMargins, true) as PDFMarginsStyle;
            return margins != null && margins.Count > 0;
        }

        public bool TryGetBackground(out PDFBackgroundStyle bg)
        {
            bg = this.GetStyleItem(PDFObjectTypes.StyleBackground, true) as PDFBackgroundStyle;
            return bg != null && bg.Count > 0;
        }

        public bool TryGetBorder(out PDFBorderStyle border)
        {
            border = this.GetStyleItem(PDFObjectTypes.StyleBorder, true) as PDFBorderStyle;
            return border != null && border.Count > 0;
        }

        public bool TryGetOverflow(out PDFOverflowStyle overflow)
        {
            overflow = this.GetStyleItem(PDFObjectTypes.StyleOverflow, true) as PDFOverflowStyle;
            return overflow != null && overflow.Count > 0;
        }

        public bool TryGetTextStyle(out PDFTextStyle text)
        {
            text = this.GetStyleItem(PDFObjectTypes.StyleText, true) as PDFTextStyle;
            return text != null && text.Count > 0;
        }

        public bool TryGetPaper(out PDFPageStyle paper)
        {
            paper = this.GetStyleItem(PDFObjectTypes.StylePage, true) as PDFPageStyle;
            return paper != null && paper.Count > 0;
        }

        public bool TryGetFill(out PDFFillStyle fill)
        {
            fill = this.GetStyleItem(PDFObjectTypes.StyleFill, true) as PDFFillStyle;
            return fill != null && fill.Count > 0;
        }

        public bool TryGetStroke(out PDFStrokeStyle stroke)
        {
            stroke = this.GetStyleItem(PDFObjectTypes.StyleStroke, true) as PDFStrokeStyle;
            return stroke != null && stroke.Count > 0;
        }

        public bool TryGetFont(out PDFFontStyle font)
        {
            font = this.GetStyleItem(PDFObjectTypes.StyleFont, true) as PDFFontStyle;
            return font != null && font.Count > 0;
        }

        public bool TryGetClipping(out PDFClipStyle clip)
        {
            clip = this.GetStyleItem(PDFObjectTypes.StyleClip, true) as PDFClipStyle;
            return clip != null && clip.Count > 0;
        }

        public bool TryGetPosition(out PDFPositionStyle pos)
        {
            pos = this.GetStyleItem(PDFObjectTypes.StylePosition, true) as PDFPositionStyle;
            return pos != null && pos.Count > 0;
        }

        public bool TryGetPadding(out PDFPaddingStyle padding)
        {
            padding = this.GetStyleItem(PDFObjectTypes.StylePadding, true) as PDFPaddingStyle;
            return padding != null && padding.Count > 0;
        }

        public bool TryGetOutline(out PDFOutlineStyle outline)
        {
            outline = this.GetStyleItem(PDFObjectTypes.StyleOutline, true) as PDFOutlineStyle;
            return outline != null && outline.Count > 0;
        }

        public bool TryGetColumns(out PDFColumnsStyle columns)
        {
            columns = this.GetStyleItem(PDFObjectTypes.StyleColumns, true) as PDFColumnsStyle;
            return columns != null && columns.Count > 0;
        }

        public bool TryGetOverlayGrid(out PDFOverlayGridStyle grid)
        {
            grid = this.GetStyleItem(PDFObjectTypes.StyleOverlayGrid, true) as PDFOverlayGridStyle;
            return grid != null && grid.Count > 0;
        }

        public bool TryGetBadgeStyle(out PDFScryberBadgeStyle badge)
        {
            badge = this.GetStyleItem(PDFObjectTypes.StyleBadge, true) as PDFScryberBadgeStyle;
            return badge != null && badge.Count > 0; 
        }

        public bool TryGetTableStyle(out PDFTableStyle table)
        {
            table = this.GetStyleItem(PDFObjectTypes.StyleTable, true) as PDFTableStyle;
            return null != table && table.Count > 0;
        }

        public bool TryGetListStyle(out PDFListStyle list)
        {
            list = this.GetStyleItem(PDFObjectTypes.StyleList, true) as PDFListStyle;
            return null != list && list.Count > 0;
        }

        public bool TryGetShapeStyle(out PDFShapeStyle shape)
        {
            shape = this.GetStyleItem(PDFObjectTypes.ShapeStyle, true) as PDFShapeStyle;
            return null != shape && shape.Count > 0;
        }

        public bool TryGetModifyStyle(out PDFModifyPageStyle style)
        {
            style = this.GetStyleItem(PDFObjectTypes.StyleModify, true) as PDFModifyPageStyle;
            return null != style && style.Count > 0;
        }

        /// <summary>
        /// Attempts to look for any style data associated with the specified type. 
        /// The style will be flattened to inclue all attributed data.
        /// Returns true if found, or false if not
        /// </summary>
        /// <param name="type"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool TryGetStyle(PDFObjectType type, out PDFStyleItem item)
        {
            item = this.GetStyleItem(type, true);
            return null != item && item.Count > 0;
        }

        /// <summary>
        /// Wrapper to support the execution of style updates (even to immutable styles). 
        /// Immutable value will always be set back to previous value even if there is an error.
        /// </summary>
        /// <param name="allowed">The anonymous block that will be executed whilst the style is mutable</param>
        public void AllowChanges(AllowedChanges allowed)
        {
            bool immuatable = this.Immutable;
            this.Immutable = false;
            try
            {
                allowed();
            }
            finally
            {
                this.Immutable = immuatable;
            }
        }


        public PDFTextRenderOptions CreateTextOptions()
        {
            if (this.Immutable && null != this._immutableData && null != this._immutableData.Text)
                return this._immutableData.Text;

            PDFTextRenderOptions text = CreateTextStyle(this);

            if (this.Immutable)
            {
                if (null == this._immutableData)
                    this._immutableData = new ImmutableStyleData();
                this._immutableData.Text = text;
            }

            return text;
        }

        private static PDFTextRenderOptions CreateTextStyle(PDFStyle style)
        {
            PDFTextRenderOptions options = new PDFTextRenderOptions();
            Styles.PDFFontStyle font;
            if (style.TryGetFont(out font))
                options.Font = font.CreateFont();
            Styles.PDFFillStyle fill;
            if (style.TryGetFill(out fill))
                options.FillBrush = fill.CreateBrush();

            Styles.PDFBackgroundStyle bg;
            if (style.TryGetBackground(out bg))
                options.Background = bg.CreateBrush();

            Styles.PDFStrokeStyle stroke;
            if (style.TryGetStroke(out stroke))
                options.Stroke = stroke.CreatePen();

            Styles.PDFTextStyle txt;
            Styles.PDFPositionStyle pos = null;
            
            style.TryGetTextStyle(out txt);
            style.TryGetPosition(out pos);

            if (txt != null)
            {
                if (txt.IsDefined(StyleKeys.FirstInsetAttr))
                    options.FirstLineInset = txt.FirstLineInset;

                if (txt.IsDefined(StyleKeys.WordSpacingAttr))
                    options.WordSpacing = txt.WordSpacing;

                if (txt.IsDefined(StyleKeys.WrappingAttr))
                    options.WrapText = txt.WrapText;

                if (txt.IsDefined(StyleKeys.FontLeadingAttr))
                    options.Leading = txt.Leading;

                if (txt.IsDefined(StyleKeys.DecorationAttr))
                    options.TextDecoration = txt.Decoration;
            }
            if (pos != null)
            {
                if (pos.IsDefined(StyleKeys.HorizontalAlignAttr))
                    options.HAlign = pos.HAlign;

                if (pos.IsDefined(StyleKeys.VerticalAlignAttr))
                    options.VAlign = pos.VAlign;
            }

            
            return options;
        }

        public PDFPageSize CreatePageSize()
        {
            PDFPageSize pgsize;

            PDFPageStyle paper;
            if (this.TryGetPaper(out paper))
                pgsize = paper.CreatePageSize();
            else
                pgsize = PDFPageSize.A4;

            //If the width and height are 
            PDFPositionStyle pos;
            if(this.TryGetPosition(out pos))
            {
                if(pos.IsDefined(StyleKeys.WidthAttr))
                {
                    pgsize.Width = pos.Width;
                }

                if (pos.IsDefined(StyleKeys.HeightAttr))
                {
                    pgsize.Height = pos.Height;
                }
            }

            return pgsize;
        }


        public Scryber.PDFPositionOptions CreatePostionOptions()
        {
            if (this.Immutable && null != this._immutableData && null != this._immutableData.Position)
                return this._immutableData.Position;

            PDFPositionStyle pos;
            PDFOverflowStyle overflow;
            PDFMarginsStyle marg;
            PDFPaddingStyle padd;
            PDFColumnsStyle cols;

            this.TryGetPosition(out pos);
            this.TryGetOverflow(out overflow);
            this.TryGetMargins(out marg);
            this.TryGetPadding(out padd);
            this.TryGetColumns(out cols);
            

            PDFPositionOptions options = new PDFPositionOptions();

            if (null != pos)
            {
                options.PositionMode = pos.PositionMode;
                
                if (pos.IsDefined(StyleKeys.XAttr))
                {
                    options.X = pos.X;
                    options.HasX = true;

                    //If we have an x value then we are either relative or absolute positioned
                    if (options.PositionMode != PositionMode.Absolute)
                        options.PositionMode = PositionMode.Relative;
                }

                if (pos.IsDefined(StyleKeys.YAttr))
                {
                    options.Y = pos.Y;
                    options.HasY = true;

                    //If we have a y value then we are either rlative or aboslute positioned
                    if (options.PositionMode != PositionMode.Absolute)
                        options.PositionMode = PositionMode.Relative;
                }

                if (pos.IsDefined(StyleKeys.WidthAttr))
                {
                    options.Width = pos.Width;
                    options.HasWidth = true;
                }

                if (pos.IsDefined(StyleKeys.HeightAttr))
                {
                    options.Height = pos.Height;
                    options.HasHeight = true;
                }

                if (pos.IsDefined(StyleKeys.ExpandWidthAttr))
                    options.FillWidth = pos.FullWidth;

                options.VAlign = pos.VAlign;
                options.HAlign = pos.HAlign;
            }

            if(null != overflow)
            {
                bool hasaction;
                if (overflow.IsDefined(StyleKeys.OverflowActionAttr))
                {
                    options.OverflowAction = overflow.Action;
                    hasaction = true;
                }
                else
                {
                    options.OverflowAction = Scryber.Drawing.OverflowAction.None;
                    hasaction = false;
                }

                if (overflow.IsDefined(StyleKeys.OverflowSplitAttr))
                    options.OverflowSplit = overflow.Split;
                else
                    options.OverflowSplit = Scryber.Drawing.OverflowSplit.Any;

                if (options.OverflowAction == OverflowAction.Clip)
                {
                    PDFClipStyle clip;
                    if (this.TryGetClipping(out clip))
                        options.ClipInset = clip.GetThickness();

                    //If overflow action was not set, but clip rect was, tben we default
                    //the action to Clip(ping)
                    if (hasaction == false)
                        options.OverflowAction = OverflowAction.Clip;
                }
                else
                    options.ClipInset = PDFThickness.Empty();
            }

            if (null != marg)
                options.Margins = marg.GetThickness();
            else
                options.Margins = PDFThickness.Empty();

            if (null != padd)
                options.Padding = padd.GetThickness();
            else
                options.Padding = PDFThickness.Empty();

            if(null != cols)
            {
                if (cols.ColumnCount > 0)
                    options.ColumnCount = cols.ColumnCount;
                if (cols.IsDefined(StyleKeys.AlleyWidthAttr))
                    options.AlleyWidth = cols.AlleyWidth;
            }

            if (this.Immutable)
            {
                if (null == this._immutableData)
                    this._immutableData = new ImmutableStyleData();
                this._immutableData.Position = options;
            }

            return options;
        }

        protected override void DoDataBind(PDFDataContext context, bool includechildren)
        {
            base.DoDataBind(context, includechildren);
            if (includechildren && this.HasItems)
                this.Items.DataBind(context);
        }

        //
        // inner classes
        //

        /// <summary>
        /// Simple parameter less delegate that will be executed so that styles can be updated even if they are set as immutable.
        /// Use as an anonymous block style.AllowChanges(delegate() {style.XXX.XX = value;}); 
        /// </summary>
        public delegate void AllowedChanges();

        
    }
}
